unit StateMachine;

{
  A Global StateMachine wich controlls the whole programm flow.
}

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, typinfo, logger;

type
  EStateTransitException = class (Exception);

  //See infos for state transition diagram
  TSystemState = (
    SS_None,                 //first state, stays until the OS displayes the main form
    SS_SelectSystems,        //first real state, where the user musst select all sub systems
    SS_SetupSystems,         //when the user has selected the systems and we generate them
    SS_WaitingForGameStart,  //waiting for the actual game to start, so that we can grab the field etc
    SS_InitializeGame,       //when the game starts, we need to parse the map etc
    SS_AnalyzeGameState,     //analyze the current state of the game, then decide what to do. this happens in the timer methode
    SS_ReactToGameState      //game state did change, we have to act
  );

  { TStateMachine }

  TStateMachine = class
    private
      FState: TSystemState;
      procedure IllegalTransit(AState: TSystemState);
      procedure LogTransit(AState: TSystemState);
    public
      constructor Create;
      destructor Destroy; override;
    public
      property State: TSystemState read FState;
    public
      procedure Transit(AState: TSystemState; ExceptionTransition: boolean = false);
  end;

var
  GlobalStateMachine: TStateMachine;

implementation

uses
  SystemManager,main;

{ TStateMachine }

procedure TStateMachine.IllegalTransit(AState: TSystemState);
var
  StateOld,StateNew: String;
begin
  {Displays an error message if an illegal transit was started, the transit gets revoked}
  StateOld := GetEnumName(TypeInfo(TSystemState), integer(FState));
  StateNew := GetEnumName(TypeInfo(TSystemState), integer(AState));
  raise EStateTransitException.Create('An illegal state transit from '+StateOld+' to '+StateNew+' was started. This transit was stopped.');
end;

procedure TStateMachine.LogTransit(AState: TSystemState);
var
  StateOld,StateNew: String;
begin
  StateOld := GetEnumName(TypeInfo(TSystemState), integer(FState));
  StateNew := GetEnumName(TypeInfo(TSystemState), integer(AState));
  GlobalSystemManager.Log.Log('Transiting from '+StateOld+' to '+StateNew,TLSS_StateMachine);
end;

constructor TStateMachine.Create;
begin
  inherited Create;
  FState := SS_None;
end;

destructor TStateMachine.Destroy;
begin
  inherited Destroy;
end;

procedure TStateMachine.Transit(AState: TSystemState;
  ExceptionTransition: boolean);
var
  OldState: TSystemState;
begin
  if AState=FState then exit;
  LogTransit(AState);
  OldState := FState;
  FState := AState;
  try
    case AState of
      SS_SelectSystems: MainForm.SetupSelectionProcess;
      SS_SetupSystems: if OldState=SS_SelectSystems then GlobalSystemManager.setup else IllegalTransit(AState);
      SS_WaitingForGameStart: GlobalSystemManager.WaitForGameStart;
      SS_InitializeGame: GlobalSystemManager.InitializeGame;
      SS_AnalyzeGameState: ; //in systemmanager.IOTimerEvent
      SS_ReactToGameState: if assigned(GlobalSystemManager.AI) then GlobalSystemManager.AI.Act;
    else
       IllegalTransit(AState);
    end;
  except
    if not ExceptionTransition then
    begin
      Transit(OldState, true);
    end;
    raise;
  end;
end;

initialization
  GlobalStateMachine := TStateMachine.Create;

finalization
  GlobalStateMachine.Free;


end.

